package com.ark.frigate.zookeeper.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
import org.springframework.util.Assert;

import java.util.concurrent.TimeUnit;

/**
 * 公平锁
 *
 * @author zengweilong
 * @date 8/6/21 3:25 PM
 */
@Slf4j
public class ZookeeperFairLock {

    private CuratorFramework curatorFramework;

    private InterProcessLock lock;

    private String lockPath;

    private Long timeout;

    /**
     * 是否可重入
     */
    private Boolean isSemaphore;


    public ZookeeperFairLock(String lockPath, Long timeout) {
        Assert.isTrue(StringUtils.isNotBlank(lockPath), "lock path is required.");
        this.lockPath = lockPath;

        this.curatorFramework = SpringUtils.getBean(CuratorFramework.class);
        Assert.isTrue(timeout > 0, "timeout is require greater than 0.");
        this.timeout = timeout;
        this.isSemaphore = true;
    }

    public ZookeeperFairLock(String lockPath, Long timeout, Boolean isSemaphore) {
        this.curatorFramework = SpringUtils.getBean(CuratorFramework.class);
        Assert.isTrue(StringUtils.isNotBlank(lockPath), "lock path is required.");
        this.lockPath = lockPath;
        Assert.isTrue(timeout > 0, "timeout is require greater than 0.");
        this.timeout = timeout;
        this.isSemaphore = isSemaphore;
    }

    public boolean lock() {
        try {
            if (isSemaphore) {
                this.lock = new InterProcessSemaphoreMutex(curatorFramework, lockPath);
            } else {
                this.lock = new InterProcessMutex(curatorFramework, lockPath);
            }
            boolean lockStatus = lock.acquire(timeout, TimeUnit.MILLISECONDS);
            log.info("add Lock {}  status = {}", lockPath, lockStatus);
            return lockStatus;
        } catch (Exception e) {
            log.error("add Lock fail.", e);
            return false;
        }
    }

    public boolean unLock() {
        if (this.lock == null) {
            log.warn("not have a lock.");
            return true;
        }
        try {
            this.lock.release();
            log.info("unlock key={}", lockPath);
            return true;
        } catch (Exception e) {
            log.error("UnLock fail.", e);
            return false;
        }
    }

}
